/*****************************************************************************
 * drivers/can/ctucanfd_pci.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 *****************************************************************************/

/*****************************************************************************
 * Included Files
 *****************************************************************************/

#include <nuttx/config.h>

#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdio.h>

#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/pci/pci.h>

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
#  include <nuttx/can/can.h>
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
#  include <nuttx/net/netdev_lowerhalf.h>
#  include <nuttx/net/can.h>
#endif

#include "ctucanfd.h"

/*****************************************************************************
 * Pre-processor Definitions
 *****************************************************************************/

/* PCI BARs */

#define CTUCANFD_BAR0         0
#define CTUCANFD_CTUCAN_BAR1  1

/* Registers per channel */

#define CTUCANFD_CTUCAN_REGS  0x4000

/* ID register in BAR0 */

#define CTUCANFD_BAR0_ID      0

/* Interrupts */

#define CTUCANFD_INT_ERR       (CTUCANFD_INT_DOI | CTUCANFD_INT_ALI | \
                                CTUCANFD_INT_BEI)

/* Supported TX buffers */

#define CTUCANFD_TXBUF_CNT    4

/* SocketCAN specific */

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
#  define CTUCANFD_TX_QUOTA   1
#  define CTUCANFD_RX_QUOTA   1
#  if CONFIG_IOB_NBUFFERS < (CTUCANFD_RX_QUOTA + CTUCANFD_TX_QUOTA)
#    error CONFIG_IOB_NBUFFERS must be > (CTUCANFD_RX_QUOTA + CTUCANFD_TX_QUOTA)
#  endif
#endif /* CONFIG_CAN_CTUCANFD_SOCKET */

#ifndef CONFIG_NET_CAN_ERRORS
#  define CTUCANFD_SOCK_RXINT (CTUCANFD_INT_RBNEI)
#else
#  define CTUCANFD_SOCK_RXINT (CTUCANFD_INT_RBNEI | CTUCANFD_INT_ERR)
#endif

/*****************************************************************************
 * Private Types
 *****************************************************************************/

/* CTUCANFD channel private data */

struct ctucanfd_can_s
{
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
  struct can_dev_s   dev;
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
  /* This holds the information visible to the NuttX network */

  struct netdev_lowerhalf_s dev;
#endif

  FAR struct pci_device_s *pcidev;
  uint64_t                 base;
  uint8_t                  txbufcnt;
};

/* CTUCANFD private data */

struct ctucanfd_driver_s
{
  FAR struct ctucanfd_can_s *devs;
  uint8_t                    count;
  uintptr_t                  bar0_base;
  uintptr_t                  canfd_base;

  /* PCI data */

  FAR struct pci_device_s   *pcidev;
  int                        irq;
  uintptr_t                  base;
};

/*****************************************************************************
 * Private Functions Definitions
 *****************************************************************************/

/* Helpers */

static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv,
                                unsigned int offset);
static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv,
                            unsigned int offset,
                            uint32_t value);

/* Common methods */

static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable);
static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable);

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV

/* CAN character device methods */

static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev);
static int  ctucanfd_chrdev_setup(FAR struct can_dev_s *dev);
static void ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev);
static void ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable);
static void ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable);
static int  ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
                                  unsigned long arg);
static int  ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev,
                                          uint16_t id);
static int  ctucanfd_chrdev_send(FAR struct can_dev_s *dev,
                                 struct can_msg_s *msg);
static bool ctucanfd_chrdev_txready(FAR struct can_dev_s *dev);
static bool ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev);
static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv);
static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv);
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/* SocketCAN methods */

static int  ctucanfd_sock_ifup(FAR struct netdev_lowerhalf_s *dev);
static int  ctucanfd_sock_ifdown(FAR struct netdev_lowerhalf_s *dev);
static int  ctucanfd_sock_transmit(FAR struct netdev_lowerhalf_s *dev,
                                   FAR netpkt_t *pkt);
static FAR netpkt_t *ctucanfd_sock_recv(FAR struct netdev_lowerhalf_s *dev);

#  ifdef CONFIG_NETDEV_IOCTL
static int  ctucanfd_sock_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
                                unsigned long arg);
#  endif

#  ifdef CONFIG_NET_CAN_ERRORS
static FAR netpkt_t *ctucanfd_sock_error(FAR struct netdev_lowerhalf_s *dev);
#  endif

static void ctucanfd_sock_interrupt(FAR struct ctucanfd_driver_s *priv);
#endif

/* Interrupts */

static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg);

/* PCI */

static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv);
static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv);
static int ctucanfd_probe(FAR struct pci_device_s *dev);

/*****************************************************************************
 * Private Data
 *****************************************************************************/

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
static const struct can_ops_s g_ctucanfd_can_ops =
{
  .co_reset         = ctucanfd_chrdev_reset,
  .co_setup         = ctucanfd_chrdev_setup,
  .co_shutdown      = ctucanfd_chrdev_shutdown,
  .co_rxint         = ctucanfd_chrdev_rxint,
  .co_txint         = ctucanfd_chrdev_txint,
  .co_ioctl         = ctucanfd_chrdev_ioctl,
  .co_remoterequest = ctucanfd_chrdev_remoterequest,
  .co_send          = ctucanfd_chrdev_send,
  .co_txready       = ctucanfd_chrdev_txready,
  .co_txempty       = ctucanfd_chrdev_txempty,
};
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
static const struct netdev_ops_s g_ctucanfd_net_ops =
{
  .ifup     = ctucanfd_sock_ifup,
  .ifdown   = ctucanfd_sock_ifdown,
  .transmit = ctucanfd_sock_transmit,
  .receive  = ctucanfd_sock_recv,
#ifdef CONFIG_NETDEV_IOCTL
  .ioctl    = ctucanfd_sock_ioctl,
#endif
};
#endif

static const struct pci_device_id_s g_ctucanfd_id_table[] =
{
  {
    PCI_DEVICE(0x1760, 0xff00),
    .driver_data = 0
  },
  { }
};

static struct pci_driver_s g_ctucanfd_drv =
{
  .id_table = g_ctucanfd_id_table,
  .probe    = ctucanfd_probe,
};

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
static uint8_t g_ctucanfd_count = 0;
#endif

/*****************************************************************************
 * Private Functions
 *****************************************************************************/

/*****************************************************************************
 * Name: ctucanfd_getreg
 *****************************************************************************/

static uint32_t ctucanfd_getreg(FAR struct ctucanfd_can_s *priv,
                                unsigned int offset)
{
  uintptr_t addr = priv->base + offset;
  return *((FAR volatile uint32_t *)addr);
}

/*****************************************************************************
 * Name: ctucanfd_putreg
 *****************************************************************************/

static void ctucanfd_putreg(FAR struct ctucanfd_can_s *priv,
                            unsigned int offset,
                            uint32_t value)
{
  uintptr_t addr = priv->base + offset;
  *((FAR volatile uint32_t *)addr) = value;
}

/*****************************************************************************
 * Name: ctucanfd_reset
 *****************************************************************************/

static void ctucanfd_reset(FAR struct ctucanfd_can_s *priv)
{
  /* Soft reset */

  ctucanfd_putreg(priv, CTUCANFD_SET_MODE, CTUCANFD_MODE_RST);
}

/*****************************************************************************
 * Name: ctucanfd_shutdown
 *****************************************************************************/

static void ctucanfd_shutdown(FAR struct ctucanfd_can_s *priv)
{
  ctucanfd_putreg(priv, CTUCANFD_SET_MODE, 0);
}

/*****************************************************************************
 * Name: ctucanfd_setup
 *****************************************************************************/

static void ctucanfd_setup(FAR struct ctucanfd_can_s *priv)
{
  uint32_t regval = 0;
  int      i;

  /* REVISIT: missing bus timings configuration.
   *
   * This driver was verified on QEMU with virtual host CAN network,
   * which doesn't need bus timings.
   * For real hardware, these registers must be properly configured !
   */

  ctucanfd_putreg(priv, CTUCANFD_BTR, 0);
  ctucanfd_putreg(priv, CTUCANFD_BTRFD, 0);

  /* Enable interrupts */

#if defined(CONFIG_CAN_ERRORS) || defined(CONFIG_NET_CAN_ERRORS)
  ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_ERR);
#else
  ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_ERR);
#endif

  /* Configure TX priority */

  for (i = 0; i < priv->txbufcnt; i++)
    {
      regval |= 1 << (CTUCANFD_TXPRIO_SHIFT * i);
    }

  ctucanfd_putreg(priv, CTUCANFD_TXPRIO, regval);

  /* Set MODE register */

  regval = ctucanfd_getreg(priv, CTUCANFD_SET_MODE);

  /* Enable CTU CAN FD */

  regval |= CTUCANFD_SET_ENA << CTUCANFD_SET_SHFIT;

  /* RX buffer automatic mode */

  regval |= CTUCANFD_MODE_RXBAM;

  /* Write SETTINGS and MODE */

  ctucanfd_putreg(priv, CTUCANFD_SET_MODE, regval);
}

/*****************************************************************************
 * Name: ctucanfd_rxint
 *****************************************************************************/

static void ctucanfd_rxint(FAR struct ctucanfd_can_s *priv, bool enable)
{
  if (enable)
    {
      ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_RBNEI);
    }
  else
    {
      ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_RBNEI);
    }
}

/*****************************************************************************
 * Name: ctucanfd_txint
 *****************************************************************************/

static void ctucanfd_txint(FAR struct ctucanfd_can_s *priv, bool enable)
{
  if (enable)
    {
      ctucanfd_putreg(priv, CTUCANFD_INTENSET, CTUCANFD_INT_TXI);
    }
  else
    {
      ctucanfd_putreg(priv, CTUCANFD_INTENCLR, CTUCANFD_INT_TXI);
    }
}

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
/*****************************************************************************
 * Name: ctucanfd_chrdev_reset
 *
 * Description:
 *   Reset the CAN device.  Called early to initialize the hardware. This
 *   function is called, before ctucanfd_setup() and on error conditions.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *  None
 *
 *****************************************************************************/

static void ctucanfd_chrdev_reset(FAR struct can_dev_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_reset(priv);
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_setup
 *
 * Description:
 *   Configure the CAN. This method is called the first time that the CAN
 *   device is opened.  This will occur when the port is first opened.
 *   This setup includes configuring and attaching CAN interrupts.
 *   All CAN interrupts are disabled upon return.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 *****************************************************************************/

static int ctucanfd_chrdev_setup(FAR struct can_dev_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_setup(priv);

  return OK;
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_shutdown
 *
 * Description:
 *   Disable the CAN.  This method is called when the CAN device is closed.
 *   This method reverses the operation the setup method.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 *****************************************************************************/

static void ctucanfd_chrdev_shutdown(FAR struct can_dev_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_shutdown(priv);
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_rxint
 *
 * Description:
 *   Call to enable or disable RX interrupts.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 *****************************************************************************/

static void ctucanfd_chrdev_rxint(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_rxint(priv, enable);
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_txint
 *
 * Description:
 *   Call to enable or disable TX interrupts.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 *****************************************************************************/

static void ctucanfd_chrdev_txint(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_txint(priv, enable);
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_ioctl
 *
 * Description:
 *   All ioctl calls will be routed through this method
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 *****************************************************************************/

static int ctucanfd_chrdev_ioctl(FAR struct can_dev_s *dev, int cmd,
                                 unsigned long arg)
{
  return -ENOTTY;
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_remoterequest
 *
 * Description:
 *   Send a remote request
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 *****************************************************************************/

static int ctucanfd_chrdev_remoterequest(FAR struct can_dev_s *dev,
                                         uint16_t id)
{
  return -ENOTSUP;
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_send
 *
 * Description:
 *    Send one can message.
 *
 *    One CAN-message consists of a maximum of 10 bytes.  A message is
 *    composed of at least the first 2 bytes (when there are no data bytes).
 *
 *    Byte 0:      Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
 *    Byte 1:      Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
 *                 Bit 4:    Remote Transmission Request (RTR)
 *                 Bits 0-3: Data Length Code (DLC)
 *    Bytes 2-10: CAN data
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 *****************************************************************************/

static int ctucanfd_chrdev_send(FAR struct can_dev_s *dev,
                                FAR struct can_msg_s *msg)
{
  FAR struct ctucanfd_can_s  *priv = (FAR struct ctucanfd_can_s *)dev;
  union ctucanfd_frame_fmt_u  fmt;
  union ctucanfd_frame_id_u   id;
  uint32_t                    regval;
  uint32_t                    offset = 0;
  int                         txidx;
  int                         i;
  uint8_t                     bytes;

  /* Get TX empty buffer */

  regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);

  for (i = 0; i < priv->txbufcnt; i++)
    {
      if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
        {
          offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i;
          txidx = i;
          break;
        }
    }

  if (offset == 0)
    {
      canerr("ERROR: TX buffer not available\n");
      return -EBUSY;
    }

  /* Reset data */

  fmt.u32 = 0;
  id.u32  = 0;

  /* Set up the DLC */

  fmt.s.dlc = msg->cm_hdr.ch_dlc;

  /* Set RTR bit */

  fmt.s.rtr = msg->cm_hdr.ch_rtr;

#ifdef CONFIG_CAN_EXTID
  if (msg->cm_hdr.ch_extid)
    {
      fmt.s.ide   = 1;
      id.s.id_ext = msg->cm_hdr.ch_id;
    }
  else
#endif
    {
      fmt.s.ide = 0;
      id.s.id   = msg->cm_hdr.ch_id;
    }

#ifdef CONFIG_CAN_FD

  /* Set CAN FD specific flags */

  fmt.s.brs     = msg->cm_hdr.ch_brs;
  fmt.s.esi_rsv = msg->cm_hdr.ch_esi;
  fmt.s.fdf = msg->cm_hdr.ch_edl;
#endif

  /* Write frame */

  ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
  ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
  ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
  ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);

  bytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
  for (i = 0; i < bytes; i++)
    {
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
                      msg->cm_data[i]);
    }

  /* Set TX ready */

  regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx));
  ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval);

  return OK;
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_txready
 *
 * Description:
 *   Return true if the CAN hardware can accept another TX message.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   True if the CAN hardware is ready to accept another TX message.
 *
 *****************************************************************************/

static bool ctucanfd_chrdev_txready(FAR struct can_dev_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;
  uint32_t                   regval;
  int                        i;

  regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);

  for (i = 0; i < priv->txbufcnt; i++)
    {
      if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
        {
          return true;
        }
    }

  return false;
}

/*****************************************************************************
 * Name: ctucanfd_chrdev_txempty
 *
 * Description:
 *   Return true if all message have been sent.  If for example, the CAN
 *   hardware implements FIFOs, then this would mean the transmit FIFO is
 *   empty.  This method is called when the driver needs to make sure that
 *   all characters are "drained" from the TX hardware before calling
 *   co_shutdown().
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   True if there are no pending TX transfers in the CAN hardware.
 *
 *****************************************************************************/

static bool ctucanfd_chrdev_txempty(FAR struct can_dev_s *dev)
{
  FAR struct ctucanfd_can_s *priv   = (FAR struct ctucanfd_can_s *)dev;
  uint32_t                   regval = 0;
  bool                       ret    = true;
  int                        i;

  regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);

  for (i = 0; i < priv->txbufcnt; i++)
    {
      if (CTUCANFD_TXSTAT_GET(regval, i) != CTUCANFD_TXSTAT_ETY)
        {
          ret = false;
          break;
        }
    }

  return ret;
}

/*****************************************************************************
 * Name: ctucanfd_chardev_receive
 *
 * Description:
 *   Receive CAN frame
 *
 *****************************************************************************/

static void ctucanfd_chardev_receive(FAR struct ctucanfd_can_s *priv)
{
  FAR struct can_dev_s        *dev    = (FAR struct can_dev_s *)priv;
  FAR struct ctucanfd_frame_s *frame;
  struct can_hdr_s             hdr;
  uint32_t                     buff[sizeof(struct ctucanfd_frame_s) / 4];
  int                          i      = 0;
  int                          ret    = 0;
  uint16_t                     frc    = 0;
  uint32_t                     regval = 0;

  /* Get frame count */

  regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT);
  frc = (regval & CTUCANFD_RXSTAT_RXFRC_MASK) >> CTUCANFD_RXSTAT_RXFRC_SHIFT;

  /* Read frames */

  while (frc-- > 0)
    {
      /* We use a pointer to buffer to avoid an unaligned pointer
       * compiler errors
       */

      frame = (struct ctucanfd_frame_s *)&buff;

      /* RX buffer in automatic mode */

      buff[0] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);

      /* Read the rest of data */

      for (i = 0; i < frame->fmt.rwcnt; i++)
        {
          buff[i + 1] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
        }

      /* Get the DLC */

      hdr.ch_dlc = frame->fmt.dlc;

      /* Get RTR bit */

      hdr.ch_rtr = frame->fmt.rtr;

#ifdef CONFIG_CAN_EXTID
      /* Get the CAN identifier. */

      hdr.ch_extid = frame->fmt.ide;

      if (hdr.ch_extid)
        {
          hdr.ch_id = frame->id.id_ext;
        }
      else
        {
          hdr.ch_id = frame->id.id;
        }
#else
      if (frame->fmt.ide)
        {
          canerr("ERROR: Received message with extended"
                 " identifier.  Dropped\n");

          continue;
        }

      hdr.ch_id = frame->id.id;
#endif

      /* Clear the error indication and unused bits */

#ifdef CONFIG_CAN_ERRORS
      hdr.ch_error = 0;
#endif
      hdr.ch_tcf   = 0;

#ifdef CONFIG_CAN_FD
      hdr.ch_esi = frame->fmt.esi_rsv;
      hdr.ch_edl = frame->fmt.fdf;
      hdr.ch_brs = frame->fmt.brs;
#else
      if (frame->fmt.fdf)
        {
          /* Drop any FD CAN messages if not supported */

          canerr("ERROR: Received CAN FD message.  Dropped\n");

          return;
        }
#endif

      /* Provide the data to the upper half driver */

      ret = can_receive(dev, &hdr, (FAR uint8_t *)&frame->data);
      if (ret < 0)
        {
          canerr("ERROR: can_receive failed %d\n", ret);
        }
    }
}

#ifdef CONFIG_CAN_ERRORS
/*****************************************************************************
 * Name: ctucanfd_chardev_error
 *****************************************************************************/

static void ctucanfd_chardev_error(FAR struct ctucanfd_can_s *priv,
                                   uint32_t stat)
{
  struct can_hdr_s hdr;
  uint16_t         errbits = 0;
  uint8_t          data[CAN_ERROR_DLC];
  int              ret;

  memset(data, 0, sizeof(data));

  /* Data overrun interrupt */

  if (stat & CTUCANFD_INT_DOI)
    {
      data[1] |= CAN_ERROR1_RXOVERFLOW;
      errbits |= CAN_ERROR_CONTROLLER;

      /* Clear data overrun */

      ctucanfd_putreg(priv, CTUCANFD_CMD,
                      CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO);
    }

  /* Arbitration lost interrupt */

  if (stat & CTUCANFD_INT_ALI)
    {
      errbits |= CAN_ERROR_LOSTARB;
      data[0]  = CAN_ERROR0_UNSPEC;
    }

  /* BUS error interrupt */

  if (stat & CTUCANFD_INT_BEI)
    {
      errbits |= CAN_ERROR_BUSERROR;
    }

  /* Report a CAN error */

  if (errbits != 0)
    {
      canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);

      /* Format the CAN header for the error report. */

      hdr.ch_id    = errbits;
      hdr.ch_dlc   = CAN_ERROR_DLC;
      hdr.ch_rtr   = 0;
      hdr.ch_error = 1;
#ifdef CONFIG_CAN_EXTID
      hdr.ch_extid = 0;
#endif
      hdr.ch_tcf   = 0;

      /* And provide the error report to the upper half logic */

      ret = can_receive(&priv->dev, &hdr, data);
      if (ret < 0)
        {
          canerr("ERROR: can_receive failed: %d\n", ret);
        }
    }
}
#endif

/*****************************************************************************
 * Name: ctucanfd_chardev_interrupt
 *****************************************************************************/

static void ctucanfd_chardev_interrupt(FAR struct ctucanfd_driver_s *priv)
{
  uint32_t stat   = 0;
  uint32_t regval = 0;
  int      i      = 0;
  int      txidx  = 0;

  for (i = 0; i < priv->count; i++)
    {
      stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);
      if (stat == 0)
        {
          continue;
        }

      /* Frame received */

      if (stat & CTUCANFD_INT_RXI)
        {
          ctucanfd_chardev_receive(&priv->devs[i]);
        }

      /* Frame transmitted */

      if (stat & CTUCANFD_INT_TXI)
        {
          regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT);

          for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++)
            {
              if (CTUCANFD_TXSTAT_GET(regval, txidx) ==
                  CTUCANFD_TXSTAT_TOK)
                {
                  can_txdone(&priv->devs[i].dev);

                  /* Mark buffer as empty */

                  regval = (CTUCANFD_TXCMD_TXCE +
                            (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)));
                  ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval);
                }
            }
        }

#ifdef CONFIG_CAN_ERRORS
      /* Handle errors */

      if (stat & CTUCANFD_INT_ERR)
        {
          ctucanfd_chardev_error(&priv->devs[i], stat);
        }
#endif

      /* Clear interrupts */

      ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat);

      /* Re-enable RX/TX interrupts */

      ctucanfd_rxint(&priv->devs[i], true);
      ctucanfd_txint(&priv->devs[i], true);
    }
}
#endif  /* CONFIG_CAN_CTUCANFD_CHARDEV */

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
/*****************************************************************************
 * Name: ctucanfd_sock_ifup
 *
 * Description:
 *   NuttX Callback: Bring up the Ethernet interface when an IP address is
 *   provided
 *
 * Input Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 *****************************************************************************/

static int ctucanfd_sock_ifup(FAR struct netdev_lowerhalf_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_reset(priv);
  ctucanfd_setup(priv);

  ctucanfd_txint(priv, true);
  ctucanfd_rxint(priv, true);

  return OK;
}

/*****************************************************************************
 * Name: ctucanfd_sock_ifdown
 *
 * Description:
 *   NuttX Callback: Stop the interface.
 *
 * Input Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *
 * Returned Value:
 *   None
 *
 *****************************************************************************/

static int ctucanfd_sock_ifdown(FAR struct netdev_lowerhalf_s *dev)
{
  FAR struct ctucanfd_can_s *priv = (FAR struct ctucanfd_can_s *)dev;

  ctucanfd_txint(priv, false);
  ctucanfd_rxint(priv, false);

  /* Shutdown */

  ctucanfd_shutdown(priv);

  return OK;
}

#  ifdef CONFIG_NETDEV_IOCTL
/*****************************************************************************
 * Name: ctucanfd_sock_ioctl
 *
 * Description:
 *   PHY ioctl command handler
 *
 * Input Parameters:
 *   dev  - Reference to the NuttX driver state structure
 *   cmd  - ioctl command
 *   arg  - Argument accompanying the command
 *
 * Returned Value:
 *   Zero (OK) on success; a negated errno value on failure.
 *
 * Assumptions:
 *
 *****************************************************************************/

static int ctucanfd_sock_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
                               unsigned long arg)
{
  return -ENOTTY;
}

#  endif  /* CONFIG_NETDEV_IOCTL */

/*****************************************************************************
 * Name: ctucanfd_sock_transmit
 *****************************************************************************/

static int ctucanfd_sock_transmit(FAR struct netdev_lowerhalf_s *dev,
                                  FAR netpkt_t *pkt)
{
  FAR struct ctucanfd_can_s  *priv = (FAR struct ctucanfd_can_s *)dev;
  union ctucanfd_frame_fmt_u  fmt;
  union ctucanfd_frame_id_u   id;
  uint32_t                    regval;
  uint32_t                    offset;
  int                         txidx;
  int                         i;

  /* Get TX empty buffer */

  regval = ctucanfd_getreg(priv, CTUCANFD_TXSTAT);

  offset = 0;
  for (i = 0; i < priv->txbufcnt; i++)
    {
      if (CTUCANFD_TXSTAT_GET(regval, i) == CTUCANFD_TXSTAT_ETY)
        {
          offset = CTUCANFD_TXT1 + CTUCANFD_TXT_SIZE * i;
          txidx = i;
          break;
        }
    }

  if (offset == 0)
    {
      canerr("ERROR: TX buffer not available\n");
      return -EBUSY;
    }

  /* Reset data */

  fmt.u32 = 0;
  id.u32  = 0;

  /* CAN 2.0 or CAN FD */

  if (netpkt_getdatalen(dev, pkt) == sizeof(struct can_frame))
    {
      FAR struct can_frame *frame = frame =
        (FAR struct can_frame *)netpkt_getdata(dev, pkt);

      /* Set up the DLC */

      fmt.s.dlc = frame->can_dlc;

      /* Set RTR bit */

      fmt.s.rtr = (frame->can_id & CAN_RTR_FLAG);

#ifdef CONFIG_NET_CAN_EXTID
      if (frame->can_id & CAN_EFF_FLAG)
        {
          fmt.s.ide   = 1;
          id.s.id_ext = (frame->can_id & 0x1ffffff);
        }
      else
#endif
        {
          fmt.s.ide = 0;
          id.s.id   = (frame->can_id & 0x07ff);
        }

      /* Write frame */

      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);

      for (i = 0; i < frame->can_dlc; i++)
        {
          ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
                          frame->data[i]);
        }
    }
#ifdef CONFIG_NET_CAN_CANFD
  else /* CAN FD frame */
    {
      FAR struct canfd_frame *frame =
        (FAR struct canfd_frame *)netpkt_getdata(dev, pkt);

      /* CAN FD frame */

      fmt.s.fdf = 1;

      /* Set up the DLC */

      fmt.s.dlc = can_bytes2dlc(frame->len);

      /* Set flags */

      fmt.s.rtr     = (frame->can_id & CAN_RTR_FLAG);
      fmt.s.brs     = (frame->flags & CANFD_BRS);
      fmt.s.esi_rsv = (frame->flags & CANFD_ESI);

#ifdef CONFIG_NET_CAN_EXTID
      if (frame->can_id & CAN_EFF_FLAG)
        {
          fmt.s.ide   = 1;
          id.s.id_ext = (frame->can_id & 0x1ffffff);
        }
      else
#endif
        {
          fmt.s.ide = 0;
          id.s.id   = (frame->can_id & 0x07ff);
        }

      /* Write frame */

      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_FMT, fmt.u32);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_ID, id.u32);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSL, 0);
      ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_TSU, 0);

      for (i = 0; i < frame->len; i++)
        {
          ctucanfd_putreg(priv, offset + CTUCANFD_TXBUF_DATA + i,
                          frame->data[i]);
        }
    }
#endif

  /* Set TX ready */

  regval = CTUCANFD_TXCMD_TXCR + (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx));
  ctucanfd_putreg(priv, CTUCANFD_TXINFOCMD, regval);

  /* All is done - free packet */

  netpkt_free(dev, pkt, NETPKT_TX);

  return OK;
}

/*****************************************************************************
 * Name: ctucanfd_sock_recv
 *****************************************************************************/

static FAR netpkt_t *ctucanfd_sock_recv(FAR struct netdev_lowerhalf_s *dev)
{
  FAR struct ctucanfd_can_s   *priv   = (FAR struct ctucanfd_can_s *)dev;
  FAR netpkt_t                *pkt    = NULL;
  FAR struct ctucanfd_frame_s *rxframe;
  uint32_t                     buff[sizeof(struct ctucanfd_frame_s) / 4];
  int                          i      = 0;
  uint32_t                     regval = 0;
  uint8_t                      bytes;

  /* Read frame if RX buffer not empty */

  regval = ctucanfd_getreg(priv, CTUCANFD_RXSETSTAT);
  if ((regval & CTUCANFD_RXSTAT_RXE) != 0)
    {
      /* Re-enable RX interrupts if no more messages to read */

      ctucanfd_rxint(priv, true);

#ifdef CONFIG_NET_CAN_ERRORS
      /* Check for errors */

      return ctucanfd_sock_error(dev);
#else
      return NULL;
#endif
    }

  /* Allocate packet */

  pkt = netpkt_alloc(dev, NETPKT_RX);
  if (!pkt)
    {
      canerr("alloc pkt_new failed\n");
      return NULL;
    }

  /* We use a pointer to buffer to avoid an unaligned pointer
   * compiler errors
   */

  rxframe = (struct ctucanfd_frame_s *)&buff;

  /* RX buffer in automatic mode */

  buff[0] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);

  /* Read the rest of data */

  for (i = 0; i < rxframe->fmt.rwcnt; i++)
    {
      buff[i + 1] = ctucanfd_getreg(priv, CTUCANFD_RXDATA);
    }

  /* CAN 2.0 or CAN FD */

#ifdef CONFIG_NET_CAN_CANFD
  if (rxframe->fmt.fdf)
    {
      struct canfd_frame *frame;

      netpkt_setdatalen(dev, pkt, sizeof(struct canfd_frame));
      frame = (FAR struct canfd_frame *)netpkt_getdata(dev, pkt);

      /* Get the DLC */

      frame->len = can_dlc2bytes(rxframe->fmt.dlc);

#ifdef CONFIG_NET_CAN_EXTID
      /* Get the CAN identifier. */

      if (rxframe->fmt.ide)
        {
          frame->can_id = rxframe->id.id_ext;
          frame->can_id |= CAN_EFF_FLAG;
        }
      else
        {
          frame->can_id = rxframe->id.id;
        }
#else
      if (rxframe->fmt.ide)
        {
          canerr("ERROR: Received message with extended"
                 " identifier.  Dropped\n");
          continue;
        }

      frame->can_id = rxframe->id.id;
#endif

      /* Extract the RTR bit */

      if (rxframe->fmt.rtr)
        {
          frame->can_id |= CAN_RTR_FLAG;
        }

      /* Get CANFD flags */

      frame->flags = 0;

      if (rxframe->fmt.esi_rsv)
        {
          frame->flags |= CANFD_ESI;
        }

      if (rxframe->fmt.brs)
        {
          frame->flags |= CANFD_BRS;
        }

      /* Get data */

      bytes = can_dlc2bytes(rxframe->fmt.dlc);
      for (i = 0; i < bytes; i++)
        {
          frame->data[i] = rxframe->data[i];
        }
    }
  else
#endif
    {
      FAR struct can_frame *frame;

      netpkt_setdatalen(dev, pkt, sizeof(struct can_frame));
      frame = (FAR struct can_frame *)netpkt_getdata(dev, pkt);

      /* Get the DLC */

      frame->can_dlc = rxframe->fmt.dlc;

#ifdef CONFIG_NET_CAN_EXTID
      /* Get the CAN identifier. */

      if (rxframe->fmt.ide)
        {
          frame->can_id = rxframe->id.id_ext;
          frame->can_id |= CAN_EFF_FLAG;
        }
      else
        {
          frame->can_id = rxframe->id.id;
        }
#else
      if (rxframe->fmt.ide)
        {
          canerr("ERROR: Received message with extended"
                 " identifier.  Dropped\n");
          continue;
        }

      frame->can_id = rxframe->id.id;
#endif

      /* Extract the RTR bit */

      if (rxframe->fmt.rtr)
        {
          frame->can_id |= CAN_RTR_FLAG;
        }

      /* Get data */

      for (i = 0; i < rxframe->fmt.dlc; i++)
        {
          frame->data[i] = rxframe->data[i];
        }
    }

  return pkt;
}

#ifdef CONFIG_NET_CAN_ERRORS
/*****************************************************************************
 * Name: ctucanfd_sock_error
 *****************************************************************************/

static FAR netpkt_t *ctucanfd_sock_error(FAR struct netdev_lowerhalf_s *dev)
{
  FAR struct ctucanfd_can_s *priv    = (FAR struct ctucanfd_can_s *) dev;
  FAR struct can_frame      *frame   = NULL;
  FAR netpkt_t              *pkt     = NULL;
  uint16_t                   errbits = 0;
  uint32_t                   stat    = 0;

  stat = ctucanfd_getreg(priv, CTUCANFD_INTSTAT);
  if ((stat & CTUCANFD_INT_ERR) == 0)
    {
      return NULL;
    }

  /* Allocate packet */

  pkt = netpkt_alloc(dev, NETPKT_RX);
  if (!pkt)
    {
      canerr("alloc pkt_new failed\n");
      return NULL;
    }

  netpkt_setdatalen(dev, pkt, sizeof(struct can_frame));
  frame = (FAR struct can_frame *)netpkt_getdata(dev, pkt);

  /* Data overrun interrupt */

  if (stat & CTUCANFD_INT_DOI)
    {
      frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
      errbits        |= CAN_ERR_CRTL;

      /* Clear data overrun */

      ctucanfd_putreg(priv, CTUCANFD_CMD,
                      (CTUCANFD_CMD_RRB | CTUCANFD_CMD_CDO));
    }

  /* Arbitration lost interrupt */

  if (stat & CTUCANFD_INT_ALI)
    {
      errbits        |= CAN_ERR_LOSTARB;
      frame->data[0]  = CAN_ERR_LOSTARB_UNSPEC;
    }

  /* BUS error interrupt */

  if (stat & CTUCANFD_INT_BEI)
    {
      errbits |= CAN_ERR_BUSERROR;
    }

  canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);

  /* Copy frame */

  frame->can_id  = errbits;
  frame->can_dlc = CAN_ERR_DLC;

  return pkt;
}
#endif

/*****************************************************************************
 * Name: ctucanfd_sock_interrupt
 *****************************************************************************/

static void ctucanfd_sock_interrupt(FAR struct ctucanfd_driver_s *priv)
{
  uint32_t stat   = 0;
  uint32_t regval = 0;
  int      i      = 0;
  int      txidx  = 0;

  for (i = 0; i < priv->count; i++)
    {
      stat = ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);
      if (stat == 0)
        {
          continue;
        }

      /* RX ready */

      if (stat & CTUCANFD_SOCK_RXINT)
        {
          netdev_lower_rxready(&priv->devs[i].dev);
        }

      /* Transmit interrupt */

      if (stat & CTUCANFD_INT_TXI)
        {
          regval = ctucanfd_getreg(&priv->devs[i], CTUCANFD_TXSTAT);

          for (txidx = 0; txidx < priv->devs[i].txbufcnt; txidx++)
            {
              if (CTUCANFD_TXSTAT_GET(regval, txidx) ==
                  CTUCANFD_TXSTAT_TOK)
                {
                  netdev_lower_txdone(&priv->devs[i].dev);

                  /* Mark buffer as empty */

                  regval = (CTUCANFD_TXCMD_TXCE +
                            (1 << (CTUCANFD_TXCMD_TXB_SHIFT + txidx)));
                  ctucanfd_putreg(&priv->devs[i], CTUCANFD_TXINFOCMD, regval);
                }
            }
        }

      /* Clear interrupts */

      ctucanfd_putreg(&priv->devs[i], CTUCANFD_INTSTAT, stat);

      /* Re-enable TX interrupts.
       * NOTE: RX interrupts are re-enabled in ctucanfd_sock_recv.
       */

      ctucanfd_txint(&priv->devs[i], true);
    }
}
#endif    /* CONFIG_CAN_CTUCANFD_SOCKET */

/*****************************************************************************
 * Name: ctucanfd_interrupt
 *
 * Description:
 *   Initialize CTUCANFD device
 *
 *****************************************************************************/

static int ctucanfd_interrupt(int irq, FAR void *context, FAR void *arg)
{
  FAR struct ctucanfd_driver_s *priv   = (FAR struct ctucanfd_driver_s *)arg;
  uint32_t                      regval = 0;
  int                           i      = 0;

  DEBUGASSERT(priv != NULL);

  for (i = 0; i < priv->count; i++)
    {
      regval |= ctucanfd_getreg(&priv->devs[i], CTUCANFD_INTSTAT);

      /* Break now if interrupt is pending */

      if (regval != 0)
        {
          /* Disable RX/TX interrupts until we are done */

          ctucanfd_rxint(&priv->devs[i], false);
          ctucanfd_txint(&priv->devs[i], false);

          break;
        }
    }

  /* Check for pending interrupts for this card */

  if (regval == 0)
    {
      return OK;
    }

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
  /* Handle character device interrupt */

  ctucanfd_chardev_interrupt(priv);
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
  /* Handle SocketCAN interrupt */

  ctucanfd_sock_interrupt(priv);
#endif

  return OK;
}

/*****************************************************************************
 * Name: ctucanfd_init
 *
 * Description:
 *   Initialize CTUCANFD device
 *
 *****************************************************************************/

static void ctucanfd_init(FAR struct ctucanfd_driver_s *priv)
{
  /* REVISIT: Only legacy IRQ supported in QEMU driver */

  priv->irq = pci_get_irq(priv->pcidev);
  irq_attach(priv->irq, ctucanfd_interrupt, priv);

  /* REVISIT: Enable card interrupts (not implemented in QEMU) */

  /* Enable interrupts */

  up_enable_irq(priv->irq);
}

/*****************************************************************************
 * Name: ctucanfd_ctucanfd_probe
 *
 * Description:
 *   Probe CTUCANFD devices on board and return the number of available chips.
 *
 *****************************************************************************/

static uint8_t ctucanfd_ctucanfd_probe(FAR struct ctucanfd_driver_s *priv)
{
  uint32_t offset;
  uint8_t  regval;

  offset = priv->bar0_base + CTUCANFD_BAR0_ID;
  pci_read_io_byte(priv->pcidev, offset, &regval);

  /* REVISIT: what is the maximum number of channels ? */

  return regval;
}

/*****************************************************************************
 * Name: ctucanfd_probe
 *
 * Description:
 *   Probe device
 *
 *****************************************************************************/

static int ctucanfd_probe(FAR struct pci_device_s *dev)
{
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
  FAR struct netdev_lowerhalf_s *netdev = NULL;
#endif
  FAR struct ctucanfd_driver_s  *priv   = NULL;
  uint8_t                        i      = 0;
  int                            ret;
#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
  uint8_t                        count;
  char                           devpath[PATH_MAX];
#endif

  /* Allocate the interface structure */

  priv = kmm_zalloc(sizeof(*priv));
  if (priv == NULL)
    {
      ret = -ENOMEM;
      return ret;
    }

  /* Initialzie PCI dev */

  priv->pcidev = dev;

  pci_set_master(dev);
  pciinfo("Enabled bus mastering\n");
  pci_enable_device(dev);
  pciinfo("Enabled memory resources\n");

  /* Control register BAR */

  priv->bar0_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_BAR0);
  if (!priv->bar0_base)
    {
      pcierr("Not found BAR0\n");
      ret = -ENODEV;
      goto errout;
    }

  /* MEM access only supported */

  if (pci_resource_flags(dev, CTUCANFD_BAR0) != PCI_RESOURCE_MEM)
    {
      ret = -ENOTSUP;
      goto errout;
    }

  priv->canfd_base = (uintptr_t)pci_map_bar(dev, CTUCANFD_CTUCAN_BAR1);
  if (!priv->canfd_base)
    {
      pcierr("Not found CANFD bar\n");
      ret = -ENODEV;
      goto errout;
    }

  /* MEM access only supported */

  if (pci_resource_flags(dev, CTUCANFD_CTUCAN_BAR1) != PCI_RESOURCE_MEM)
    {
      ret = -ENOTSUP;
      goto errout;
    }

  /* Get number of CTUCANFD chips */

  priv->count = ctucanfd_ctucanfd_probe(priv);

  pciinfo("detected %d CTUCANFD channels\n", priv->count);

  /* Allocate CTUCANFD devices */

  priv->devs = kmm_zalloc(sizeof(struct ctucanfd_can_s) * priv->count);
  if (priv->devs == NULL)
    {
      ret = -ENOMEM;
      goto errout;
    }

  /* Common initialziation for all channels */

  ctucanfd_init(priv);

  /* Handle all CTUCANFD devices */

  for (i = 0; i < priv->count; i++)
    {
      /* Common initialization */

      priv->devs[i].base     = priv->canfd_base + (CTUCANFD_CTUCAN_REGS * i);
      priv->devs[i].pcidev   = dev;
      priv->devs[i].txbufcnt = CTUCANFD_TXBUF_CNT;

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
      count = g_ctucanfd_count++;

      /* Get devpath for this CTUCANFD device */

      snprintf(devpath, PATH_MAX, "/dev/can%d", count);

      /* Initialize CTUCANFD channel */

      priv->devs[i].dev.cd_ops  = &g_ctucanfd_can_ops;
      priv->devs[i].dev.cd_priv = &priv->devs[i];

      /* Register CAN device */

      ret = can_register(devpath, &priv->devs[i].dev);
      if (ret < 0)
        {
          pcierr("ERROR: failed to register count=%d, %d\n", i, ret);
          goto errout;
        }
#endif

#ifdef CONFIG_CAN_CTUCANFD_SOCKET
      netdev = &priv->devs[i].dev;

      /* Register the network device */

      netdev->quota[NETPKT_TX] = CTUCANFD_TX_QUOTA;
      netdev->quota[NETPKT_RX] = CTUCANFD_RX_QUOTA;
      netdev->ops = &g_ctucanfd_net_ops;

      /* Put the interface in the down state.  This usually amounts to
       * resetting the device and/or calling ctucanfd_sock_ifdown().
       */

      ctucanfd_sock_ifdown(&priv->devs[i].dev);

      /* Register the network interface. */

      ret = netdev_lower_register(netdev, NET_LL_CAN);
      if (ret < 0)
        {
          pcierr("ERROR: failed to register count=%d, %d\n", i, ret);
          goto errout;
        }
#endif
    }

  return OK;

errout:
  for (i = 0; i < priv->count; i++)
    {
      if (priv->devs[i].pcidev)
        {
#ifdef CONFIG_CAN_CTUCANFD_SOCKET
          netdev = &priv->devs[i].dev;
          netdev_lower_unregister(netdev);
#endif

#ifdef CONFIG_CAN_CTUCANFD_CHARDEV
          unregister_driver(devpath);
#endif
        }
    }

  kmm_free(priv->devs);
  kmm_free(priv);

  return ret;
}

/*****************************************************************************
 * Public Functions
 *****************************************************************************/

/*****************************************************************************
 * Name: pci_ctucanfd_init
 *
 * Description:
 *   Register a pci driver
 *
 *****************************************************************************/

int pci_ctucanfd_init(void)
{
  return pci_register_driver(&g_ctucanfd_drv);
}
